
/* 
 *
 * (c) BILE AT BERNARD <webmaster@classinter.com>
 *
 * For the full copyright and license information, please view the LICENSE in the META-INF directory
 * file that was distributed with this source code.
 */

package com.bill.ensi.sti.ssu.jcm;

 
 
import java.util.HashMap;
import java.util.List;

import javax.smartcardio.Card;
import javax.smartcardio.CardChannel;
import javax.smartcardio.CardException;
import javax.smartcardio.CardTerminal;
import javax.smartcardio.CardTerminals;
import javax.smartcardio.CommandAPDU;
import javax.smartcardio.ResponseAPDU;
import javax.smartcardio.TerminalFactory;
import javax.swing.event.EventListenerList;

import com.bill.ensi.sti.ssu.jcminterface.JavacardManagerInterface;
import com.sun.javacard.apduio.Apdu;
 
public class SmartCardManager {
	javax.smartcardio.Card   smartcard;
private Apdu apdu;
private CardTerminal terminal ;
private byte[] applicationid;
private byte CLA ;
private byte INS ;
private byte P1 ;
private byte P2;
private byte[] data;
private int le;
private int lc;
private boolean waitforcarddetection;
private String swcommentlanguage="fr";
public final static String SW_COMMENT_LANGUAGE_FRENCH="fr";
public final static String SW_COMMENT_LANGUAGE_ENGLISH="en";
public final static String SW_COMMENT_LANGUAGE_SPANISH="es";

public HashMap<Object, String> swSignification=new HashMap<Object, String>();
 
 
public void setCustomSwSignification(
		HashMap<Object, String> customSwSignification) {
	 
	this.swSignification.putAll(customSwSignification);
}
protected final EventListenerList listeners = new EventListenerList();
public String getExplanation(int sw){
	return this.swSignification.get(this.getSwcommentlanguage()+sw);
}

public SmartCardManager(boolean waitforcarddetection,String swlanguage){
	this.waitforcarddetection=waitforcarddetection; 
	this.swSignification.put("en"+0x6999, "Applet selection failed");
	  
	 
	this.swSignification.put("en"+0x6100, "Response bytes remaining");

	this.swSignification.put("en"+0x6E00, "CLA value not supported");
	this.swSignification.put("en"+0x6884, "Command chaining not supported");
	this.swSignification.put("en"+0x6986, "Command not allowed (no current EF)");
	this.swSignification.put("en"+0x6985, "Conditions of use not satisfied");
	this.swSignification.put("en"+0x6C00, "Correct Expected Length (Le)");
	this.swSignification.put("en"+0x6984, "Data invalid ");
	this.swSignification.put("en"+0x6A84, "Not enough memory space in the file ");

	this.swSignification.put("en"+0x6983, "File invalid");
	this.swSignification.put("en"+0x6A82, "File not found ");
	this.swSignification.put("en"+0x6A81, "Function not supported ");
	this.swSignification.put("en"+0x6A86, "Incorrect parameters (P1,P2)  ");
	this.swSignification.put("en"+0x6D00, "INS value not supported");
	this.swSignification.put("en"+0x6883, " Last command in chain expected");
	this.swSignification.put("en"+0x6881, "Card does not support the operation on the specified logical channel");
	this.swSignification.put("en"+0x9000, "SW_NO_ERROR");
	this.swSignification.put("en"+0x6A83, "Record not found ");
	this.swSignification.put("en"+0x6882, "Card does not support secure messaging ");
	 
	this.swSignification.put("en"+0x6982, "Security condition not satisfied ");
	this.swSignification.put("en"+0x6F00, " No precise diagnosis");
	this.swSignification.put("en"+0x6200, " Warning, card state unchanged");

	this.swSignification.put("en"+0x6A80, " Wrong data");

	this.swSignification.put("en"+0x6700, "Wrong length ");
	this.swSignification.put("en"+0x6B00, "Incorrect parameters (P1,P2)  ");
	this.swSignification.put("fr"+0x6999 ,"S�lection Applet �chou� " ) ;


	this.swSignification.put("fr"+0x6100 , " octets de r�ponse restants " ) ;

	this.swSignification.put("fr"+0x6E00 , " valeur de CLA pas pris en charge " ) ;
	this.swSignification.put("fr"+0x6884 , " commande de cha�nage pas pris en charge " ) ;
	this.swSignification.put("fr"+0x6986 , " Commande non autoris�e (pas de FE actif ) " ) ;
	this.swSignification.put("fr"+0x6985 , " Conditions d'utilisation non satisfaites " ) ;
	this.swSignification.put("fr"+0x6C00 , " Corriger dur�e pr�vue(Le) " ) ;
	this.swSignification.put("fr"+0x6984 , " donn�es non valide" ) ;
	this.swSignification.put("fr"+0x6A84 , " espace m�moire insuffisant dans le fichier" ) ;

	this.swSignification.put("fr"+0x6983 , "Fichier non valide" ) ;
	this.swSignification.put("fr"+0x6A82 , " Fichier non trouv� " ) ;
	this.swSignification.put("fr"+0x6A81 , " Fonction non prise en charge " ) ;
	this.swSignification.put("fr"+0x6A86 , " Param�tres incorrects ( P1 , P2 ) " ) ;
	this.swSignification.put("fr"+0x6D00 , " valeur INS pas pris en charge " ) ;
	this.swSignification.put("fr"+ 0x6883 , " Derni�re commande de la cha�ne attendue " ) ;
	this.swSignification.put("fr"+ 0x6881 , " La carte ne prend pas en charge l'op�ration sur le canal logique sp�cifi� " ) ;
	this.swSignification.put("fr"+ 0x9000 , " PAS d'erreur! " ) ;
	this.swSignification.put("fr"+0x6A83 , " Enregistrement non trouv� " ) ;
	this.swSignification.put("fr"+ 0x6882 , " La carte ne prend pas en charge la messagerie s�curis�e " ) ;
	 
	this.swSignification.put("fr"+ 0x6982 , "condition de s�curit� n'est pas satisfait" ) ;
	this.swSignification.put("fr"+ 0x6F00 , " Pas de diagnostic pr�cis " ) ;
	this.swSignification.put("fr"+ 0x6200 , "Attention , �tat ??de la carte inchang�e " ) ;

	this.swSignification.put("fr"+ 0x6A80 , " donn�es erron�es " ) ;

	this.swSignification.put("fr"+ 0x6700 , " Longueur erron�e " ) ;
	this.swSignification.put("fr"+ 0x6B00 , " Param�tres incorrects ( P1 , P2 ) " ) ;
	this.swSignification.put ( "es" +0x6999 , " selecci�n de subprogramas �chou� ");


	this.swSignification.put ( "es" +0x6100, " restants octetos de r�ponse ");

	this.swSignification.put ( "es"+ 0x6E00 , " valeur de CLA pas pris en charge ");
	this.swSignification.put ( "es"+ 0x6884 , " commande de cha�nage pas pris en charge ");
	this.swSignification.put ( "es"+ 0x6986 , " Commande no autoris�e ( pas de FE actif )");
	this.swSignification.put ( "es"+ 0x6985 , "Condiciones d' utilizaci�n no satisfaites ");
	this.swSignification.put ( "es"+ 0x6C00 ," corriger dur�e pr�vue (Le )");
	this.swSignification.put ( "es"+ 0x6984 , " donn�es no valide ");
	this.swSignification.put ( "es" +0x6A84 ," espace m�moire insuffisant dans le fichier ");

	this.swSignification.put ( "es" +0x6983 , " Fichier no valide ");
	this.swSignification.put ( "es" +0x6A82 ," Fichier no trouv� ");
	this.swSignification.put ( "es"+ 0x6A81, " Fonction no prise en charge ");
	this.swSignification.put ( "es" +0x6A86, " par�metros incorrects (P1 , P2 )");
	this.swSignification.put ( "es" +0x6D00 , " valeur INS pas pris en charge ");
	this.swSignification.put ( "es" + 0x6883, " Derni�re commande de la Cha�ne attendue ");
	this.swSignification.put ( "es" + 0x6881 , " La carta pas prend ne en charge l' op�ration sur le sp�cifi� logique canales" );
	this.swSignification.put ( "es" + 0x9000, " NO ERROR ");
	this.swSignification.put ( "es" +0x6A83 ," Enregistrement no trouv� ");
	this.swSignification.put ( "es" + 0x6882 , " La carta pas prend ne Es responsable la messagerie s�curis�e " );
	 
	this.swSignification.put ( "es" + 0x6982 , "condici�n de s�curit� n'est pas satisfait ");
	this.swSignification.put ( "es" + 0x6F00 , "Pas pr�cis diagn�stico de ");
	this.swSignification.put ( "es" + 0x6200 , " Atenci�n, �tat ??de la carta inchang�e ? ");

	this.swSignification.put ( "es" + 0x6A80, " donn�es erron�es ");

	this.swSignification.put ( "es" + 0x6700, " Longueur erron�e ");
	this.swSignification.put ( "es" + 0x6B00, " par�metros incorrects (P1 , P2 )");
	 
		this.swcommentlanguage=swlanguage;
}
public SmartCardManager(boolean waitforcarddetection){
 this(waitforcarddetection,"fr");
}
public SmartCardManager(){
	this(false,"fr");
}
/**
 * redemarre 
 */
public void restart(){
	for (JavacardManagerInterface listener : getJavacardManagerInterfaceListener()) {
		listener.onRestart(this, waitforcarddetection);
		
	}
	 this.start();
}

public boolean isWaitforcarddetection() {
	return waitforcarddetection;
}
public void setWaitforcarddetection(boolean waitforcarddetection) {
	this.waitforcarddetection = waitforcarddetection;
}
public void start(){
	for (JavacardManagerInterface listener : getJavacardManagerInterfaceListener()) {
		listener.onStart(this, waitforcarddetection);
		
	}
	if(waitforcarddetection){
	while( !fetchTerminal()){
		System.err.println("aucun lecteur n'est detect�");
	}
	while(!fetchSmartcard())System.err.println("aucune n'est detect�e dans le lecteur");
	}else{
		fetchTerminal();
		if(null==terminal){
			System.err.println("aucun terminal n'a �t� detect�\nVeuillez inserer un terminal puis recommencer");
			System.exit(-1);
		}
		fetchSmartcard();
		if(null==smartcard){
			System.err.println("aucune puce n'et pr�sente dans le terminal \nVeuillez inserer une puce puis recommencer");
			System.exit(-1);
		}
		
	}
	this.apdu=new Apdu();
	this.le=this.lc=this.INS=this.CLA=this.P1=this.P2=-1;
}

protected boolean fetchSmartcard(){
	if(null==this.terminal)return false;
	try {
		if(!this.terminal.isCardPresent())return false;
	} catch (CardException e) {
		// TODO Auto-generated catch block
		return false;
	}
	try {
		this.smartcard=terminal.connect("*");
	} catch (CardException e) {
		// TODO Auto-generated catch block
		for (JavacardManagerInterface listener : getJavacardManagerInterfaceListener()) {
			listener.onException(this, e);
			
		}
	}
	
	for (JavacardManagerInterface listener : getJavacardManagerInterfaceListener()) {
		listener.onSmartcardDetected(smartcard);
		
	}
	 
	return true;
}
protected boolean fetchTerminal(){
	 List<CardTerminal> terminals = null;  
		 
			 TerminalFactory factory = TerminalFactory.getDefault();
		
			CardTerminals cadterminal = factory.terminals();
			 
				try{
			terminals = cadterminal.list();
			 
			
				}catch(CardException e3){
					 return false;
				}
				 
			 
			this.terminal = terminals.get(0);
			for (JavacardManagerInterface listener : getJavacardManagerInterfaceListener()) {
				listener.onTerminalDetected(terminal, terminal.getName());
			}
			 
	return true;
}
public void selectApplication(){
	if(null!=applicationid){
		this.le=lc=0;
		for (JavacardManagerInterface listener : getJavacardManagerInterfaceListener()) {
			listener.onApplicationSelecting(this,applicationid);
		}
		sendData(this.getApplicationid());
		
	}
}
public void powerUp(){
	byte[] atr=null;
	atr=smartcard.getATR().getBytes();
	atr=subByte(atr, 0, atr.length-2);//avoid the 2 bytes status word 
	for (JavacardManagerInterface listener : getJavacardManagerInterfaceListener()) {
		listener.onSmartcardPowerUp(smartcard,atr);
	}
}
public void powerDown(){
	try {
		smartcard.disconnect(true);
	} catch (CardException e) {
		// TODO Auto-generated catch block
		for (JavacardManagerInterface listener : getJavacardManagerInterfaceListener()) {
			listener.onException(this, e);
		}
	}
	for (JavacardManagerInterface listener : getJavacardManagerInterfaceListener()) {
		listener.onSmartcardPowerDown(smartcard);
	}
}
public void sendData(){
	for (JavacardManagerInterface listener : getJavacardManagerInterfaceListener()) {
		listener.onSendingData(this,CLA, INS, P1, P2, data);
	
	}
	apdu.command[Apdu.CLA]=this.CLA;
	apdu.command[Apdu.INS]=this.INS;
	apdu.command[Apdu.P1]=this.P1;
	apdu.command[Apdu.P2]=this.P2;
 
	apdu.setLc(this.getLc());
	apdu.setLe(this.getLe());
	apdu.setDataIn(this.getData());
	
	try {
		 byte[] b=apdu.getCommandApduBytes();
		 System.out.println("getCommandApduBytes="+byte2String(b));
		 CardChannel channel = this.smartcard.getBasicChannel();
		 ResponseAPDU r = channel.transmit(new CommandAPDU(b));
			 
		byte[] receivedata=r.getData();
		
		for (JavacardManagerInterface listener : getJavacardManagerInterfaceListener()) {
			listener.onDataReceived(this,receivedata, r.getSW(), INS);
			listener.onSWset(this,r.getSW());
			listener.onSWset(this,Integer.toHexString(r.getSW()));
			String code=this.swcommentlanguage+r.getSW();
			String swcomment=null;
			  if(this.swSignification.containsKey(code))
				swcomment=this.swSignification.get(code);
			
			listener.onSWset(this,r.getSW(),swcomment);
			listener.onSWset(this,Integer.toHexString(r.getSW()),swcomment);
		}
	} catch (   Exception e2) {
		// TODO Auto-generated catch block
		for (JavacardManagerInterface listener : getJavacardManagerInterfaceListener()) {
			listener.onException(this, e2);
		} 
	}
	 
}
public HashMap<Object, String> getSwSignification() {
	return swSignification;
}

public void setSwSignification(HashMap<Object, String> swSignification) {
	this.swSignification = swSignification;
}

public static String byte2String(byte[] b) {
	// TODO Auto-generated method stub
	String res="0x";
	for (byte c : b) {
		int i;
		if((c&128)==128)
			i=c+256;
		else
			i=c;
		if(i<10)
			res+="0"+i;
		else{
			res+=Integer.toHexString(i);
		}
	}
	return res;
}
public String getSwcommentlanguage() {
	return swcommentlanguage;
}
public void setSwcommentlanguage(String swcommentlanguage) {
	this.swcommentlanguage = swcommentlanguage;
}
/**
 * 
 * @param datasource <blockquote>la source de donn�es</blockquote>
 * @param debut <blockquote>l'offset de donn�e � prendre
 * @param lenght <blockquote>la taille de la plage de copie</blockquote>
 * @return
 */
public static byte[] subByte(byte[] dataOut, int start, int lenght) {
	// TODO Auto-generated method stub
	if(null==dataOut) return null;
	if(start>lenght) return null;
	if(lenght>dataOut.length) return null;
	 
	byte[] ret=new byte[lenght-start];
	for(int i=start,j=0;i<lenght;i++,j++)
		ret[j]=dataOut[i];
	return ret;
}
/**
 * 
 * @param CLA  <blockquote>la classe de l'instruction</blockquote>
 * @param INS <blockquote>le code de l'instruction</blockquote>
 * @param P1 <blockquote>le param�tre P1</blockquote>
 * @param P2  <blockquote>le param�tre P2</blockquote>
 * @param LE   <blockquote>la longuer maximum des donn�e attendue</blockquote>
 * @param LC  <blockquote>la taille de la data</blockquote>
 * @param data <blockquote>la donn�e optionnelle � envoyer</blockquote>
 */
public void sendData( byte CLA,byte INS,byte P1,byte P2,byte LE,byte LC,byte[] data){
	this.setCLA(CLA);
	this.setINS(INS);
	this.setP1(P1);
	this.setP2(P2);
	this.setLe(LE);
	this.setLc(LC);
	this.setData(data);
	this.sendData();
	
}
/**
 * 
 * @param CLA  <blockquote>la classe de l'instruction</blockquote>
 * @param INS <blockquote>le code de l'instruction</blockquote>
 * @param P1 <blockquote>le param�tre P1</blockquote>
 * @param P2  <blockquote>le param�tre P2</blockquote>
 * @param LE   <blockquote>la longuer maximum des donn�e attendue</blockquote>
 *
 */
public void sendData( byte CLA,byte INS,byte P1,byte P2,byte LE){
	this.setCLA(CLA);
	this.setINS(INS);
	this.setP1(P1);
	this.setP2(P2);
	this.setLe(LE);
	this.setLc(0);
	this.setData(new byte[]{});
	this.sendData();
	
}
/**
 * 
 * @param CLA  <blockquote>la classe de l'instruction</blockquote>
 * @param INS <blockquote>le code de l'instruction</blockquote>
 * @param P1 <blockquote>le param�tre P1</blockquote>
 * @param P2  <blockquote>le param�tre P2</blockquote>
 * 
 *
 */
public void sendData( byte CLA,byte INS,byte P1,byte P2){
	this.setCLA(CLA);
	this.setINS(INS);
	this.setP1(P1);
	this.setP2(P2);
	this.setLe(0);
	this.setLc(0);
	this.setData(new byte[]{});
	this.sendData();
	
}
public void sendData(byte[] data){
	this.data=data;
	this.sendData();
}
/**
 * 
 * @param applicationid
 */
public void selectApplication(byte[] applicationid){
	this.setApplicationid(applicationid);
	selectApplication();
}
/**
 * 
 * @param intf
 * ajoute un JavacardManagerInterface listener au manager
 */
public void addJavacardManagerInterfaceListener(JavacardManagerInterface listener){
	listeners.add(JavacardManagerInterface.class, listener);
}
public void removeJavacardManagerInterfaceListener(JavacardManagerInterface listener){
	listeners.remove(JavacardManagerInterface.class, listener);
}
 
 public JavacardManagerInterface[] getJavacardManagerInterfaceListener() {
    return listeners.getListeners( JavacardManagerInterface.class);
}
public Card getSmartcard() {
	return smartcard;
}
public void setSmartcard(Card smartcard) {
	this.smartcard = smartcard;
}
public Apdu getApdu() {
	return apdu;
}
public void setApdu(Apdu apdu) {
	this.apdu = apdu;
}
public CardTerminal getTerminal() {
	return terminal;
}
public void setTerminal(CardTerminal terminal) {
	this.terminal = terminal;
}
public byte[] getApplicationid() {
	return applicationid;
}
public void setApplicationid(byte[] applicationid) {
	this.applicationid = applicationid;
}
public byte getCLA() {
	return CLA;
}
public void setCLA(byte cLA) {
	CLA = cLA;
}
public byte getINS() {
	return INS;
}
public void setINS(byte iNS) {
	INS = iNS;
}
public byte getP1() {
	return P1;
}
public void setP1(byte p1) {
	P1 = p1;
}
public byte getP2() {
	return P2;
}
public void setP2(byte p2) {
	P2 = p2;
}
public byte[] getData() {
	return data;
}
public void setData(byte[] data) {
	this.data = data;
}

public int getLe() {
	return le;
}
public void setLe(int le) {
	this.le = le;
}
public int getLc() {
	return lc;
}
public void setLc(int lc) {
	this.lc = lc;
}

}
